#define DEBUG 1

#include "dyld-interpose.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <libgen.h>
#include <spawn.h>

int desync_fsync(int fd) {
	//unsetenv("DYLD_INSERT_LIBRARIES");
#if DEBUG > 0
	fprintf(stderr, "libdesync: Ignoring fsync()\n");
#endif
	return 0;
}
DYLD_INTERPOSE(desync_fsync, fsync)

void desync_sync(void) {
	//unsetenv("DYLD_INSERT_LIBRARIES");
#if DEBUG > 0
	fprintf(stderr, "libdesync: Ignoring sync()\n");
#endif
	return;
}
DYLD_INTERPOSE(desync_sync, sync)

int desync_open(const char* pathname, int flags, ...) {
	va_list ap;
	mode_t mode;

	//unsetenv("DYLD_INSERT_LIBRARIES");

	va_start(ap, flags);
	mode = (mode_t) va_arg(ap, int);
	va_end(ap);

#if DEBUG > 0
	if (flags & O_SYNC) {
		fprintf(stderr, "libdesync: Ignoring O_SYNC arg to open()\n");
	}
	if (flags & O_DSYNC) {
		fprintf(stderr, "libdesync: Ignoring O_DSYNC arg to open()\n");
	}
#endif
	flags &= ~(O_SYNC|O_DSYNC);

	return open(pathname, flags, mode);
}
DYLD_INTERPOSE(desync_open, open)

/*
int desync_fdatasync(int fd) {
	return 0;
}
DYLD_INTERPOSE(desync_fdatasync, fdatasync)
*/

int desync_msync(void *addr, size_t length, int flags) {
	//unsetenv("DYLD_INSERT_LIBRARIES");
#if DEBUG > 0
	fprintf(stderr, "libdesync: Ignoring msync()\n");
#endif
	return 0;
}
DYLD_INTERPOSE(desync_msync, msync)

#if 0
/*
 * I really, really, want this to work but there's no way to pass along
 * arbitrary varargs.  See the comp.lang.c FAQ #15.12:
 * <http://c-faq.com/varargs/handoff.html>
 */
int desync_fcntl(int fildes, int cmd, ...) {
	if (cmd == F_FULLFSYNC) {
		return 0;
	}

	return fcntl(fildes, cmd, ...);
}
#endif

/*
 * Then again:
 * <http://www.opensource.apple.com/source/xnu/xnu-2050.18.24/libsyscall/wrappers/cancelable/fcntl-base.c>
 */
#ifdef F_FULLFSYNC
int desync_fcntl(int fd, int cmd, ...) {
	va_list	ap;
	void *arg;

	//unsetenv("DYLD_INSERT_LIBRARIES");

	// Not in switch so I don't have to deal with varargs macros
	if (cmd == F_FULLFSYNC) {
#if DEBUG > 0
		fprintf(stderr, "libdesync: Ignoring fcntl(fd, F_FULLFSYNC)\n");
#endif
		return 0;
	}

	va_start(ap, cmd);
	switch(cmd) {
		/* All these ifdefs are a little ridiculous, but better safe
		 * than sorry.  That said, if none of them exist, then I've got
		 * a syntax error, so I'll throw in F_FULLFSYNC, which we know
		 * already exists, otherwise this section would be ifdef'd out,
		 * and is an effective noop, since we've already returned if
		 * it's the cmd.
		 */
		case F_FULLFSYNC:
#ifdef F_GETLK
		case F_GETLK:
#endif
#ifdef F_SETLK
		case F_SETLK:
#endif
#ifdef F_SETLKW
		case F_SETLKW:
#endif
#ifdef F_SETLKWTIMEOUT
		case F_SETLKWTIMEOUT:
#endif
#ifdef F_PREALLOCATE
		case F_PREALLOCATE:
#endif
#ifdef F_SETSIZE
		case F_SETSIZE:
#endif
#ifdef F_RDADVISE
		case F_RDADVISE:
#endif
#ifdef F_LOG2PHYS
		case F_LOG2PHYS:
#endif
#ifdef F_LOG2PHYS_EXT
		case F_LOG2PHYS_EXT:
#endif
#ifdef F_GETPATH
		case F_GETPATH:
#endif
#ifdef F_GETPATH_MTMINFO
		case F_GETPATH_MTMINFO:
#endif
#ifdef F_PATHPKG_CHECK
		case F_PATHPKG_CHECK:
#endif
#ifdef F_OPENFROM
		case F_OPENFROM:
#endif
#ifdef F_UNLINKFROM
		case F_UNLINKFROM:
#endif
#ifdef F_ADDSIGS
		case F_ADDSIGS:
#endif
#ifdef F_ADDFILESIGS
		case F_ADDFILESIGS:
#endif
#ifdef F_FINDSIGS
		case F_FINDSIGS:
#endif
			arg = va_arg(ap, void *);
			break;
		default:
			arg = (void *)((unsigned long)va_arg(ap, int));
			break;
	}
	va_end(ap);
	return (fcntl(fd, cmd, arg));
}
DYLD_INTERPOSE(desync_fcntl, fcntl)
#endif

int desync_execve(const char *path, char *const argv[], char *const envp[]) {
#if DEBUG > 0
	fprintf(stderr, "libdesync: Hijacking execve()\n");
#endif
	if ( strcmp(basename((char *)path), "plugin-container") == 0 ) {
#if DEBUG > 0
		fprintf(stderr, "libdesync: disabling for plugin-container\n");
#endif
		unsetenv("DYLD_INSERT_LIBRARIES");
	}
	return execve(path, argv, envp);
}
DYLD_INTERPOSE(desync_execve, execve)


int desync_posix_spawn(pid_t *restrict pid, const char *restrict path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *restrict attrp, char *const argv[restrict], char *const envp[restrict]) {
#if DEBUG > 0
	fprintf(stderr, "libdesync: Hijacking posix_spawn()\n");
#endif
	if ( strcmp(basename((char *)path), "plugin-container") == 0 ) {
#if DEBUG > 0
		fprintf(stderr, "libdesync: disabling for plugin-container\n");
#endif
		unsetenv("DYLD_INSERT_LIBRARIES");
	}
	return posix_spawn(pid, path, file_actions, attrp, argv, envp);
}
DYLD_INTERPOSE(desync_posix_spawn, posix_spawn)

int desync_posix_spawnp(pid_t *restrict pid, const char *restrict file, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *restrict attrp, char *const argv[restrict], char *const envp[restrict]) {
#if DEBUG > 0
	fprintf(stderr, "libdesync: Hijacking posix_spawnp()\n");
#endif
	if ( strcmp(basename((char *)file), "plugin-container") == 0 ) {
#if DEBUG > 0
		fprintf(stderr, "libdesync: disabling for plugin-container\n");
#endif
		unsetenv("DYLD_INSERT_LIBRARIES");
	}
	return posix_spawnp(pid, file, file_actions, attrp, argv, envp);
}
DYLD_INTERPOSE(desync_posix_spawnp, posix_spawnp)
